//
//  SearchCitiViewPresentable.swift
//  CitySearch
//
//  Created by guotonglin on 2020/6/1.
//  Copyright © 2020 guotonglin. All rights reserved.
//

import Foundation
import RxCocoa
import RxSwift

protocol SearchCitiViewPresentable {
    typealias Input = (
        searchText: Driver<String>,
        citySelected: Driver<CityViewModel>
    )
    typealias Output = (
        cityes: Driver<[CityItemsSection]>, ()
    )
    
    typealias ViewModelBuilder = (SearchCitiViewPresentable.Input) -> SearchCitiViewPresentable
    
    var input: SearchCitiViewPresentable.Input { get }
    var output: SearchCitiViewPresentable.Output { get }
}

class SearchCityViewModel: SearchCitiViewPresentable {

    // 定义ViewModel 的输入和输出信号
    var input: SearchCitiViewPresentable.Input
    var output: SearchCitiViewPresentable.Output
    
    var airportService: AirportApi
    
    // 定义可以发出并携带 Set<AirportModel>数据的信号  类型
    typealias State = (airports: BehaviorRelay<Set<AirportModel>>, ())
    
    // 用于发出信号  例如： 从服务器上下拉数据完成
    private let state: State = (airports: BehaviorRelay(value: []), ())
    
    
    /// 此处的逻辑是:
    /// 1: City模块中 的 cell 被点击需要跳转到 Airport 模块
    /// 2: Cell 被点击，数据处理后，使用 PublishRelay  外发一个信号，
    /// 3: PublishRelay 发出的信号 强转为 Driver 供外部订阅
    /// 4: 在 Coordinator 中订阅 Driver ，跳转到 Airport模块
    
    // 定义可以发出并携带 Set<AirportModel>数据的信号  类型
    private typealias RoutingAction = (citySelectedRelay: PublishRelay<Set<AirportModel>>, ())
    
    // 用于发出信号，例如： Cell 被点击则发出一个值
    private let routingAction: RoutingAction = (citySelectedRelay: PublishRelay(), ())

    //  定义一个 Driver 类型
    typealias Routing = (citySelected: Driver<Set<AirportModel>>, ())
    
    // 订阅 routingAction 发出的信号 转为 Driver, 外部可以 订阅
    lazy var router: Routing = (citySelected: routingAction.citySelectedRelay.asDriver(onErrorDriveWith: .empty()), ())
    
    private let bag:DisposeBag = DisposeBag()
    
    init(input: SearchCitiViewPresentable.Input, airportService: AirportApi) {
        
        self.airportService = airportService
        self.input = input
        self.output = SearchCityViewModel.output(input: input, state: state)
        self.process()
    }
}

private extension SearchCityViewModel {
    
    static func output(input: SearchCitiViewPresentable.Input, state: State) -> SearchCitiViewPresentable.Output {

        // 对 searchText 信号源做过滤
        let searchTextAbservable = input.searchText
            .debounce(.milliseconds(500))
            .distinctUntilChanged()
            .skip(1)
            .asObservable()
            .share(replay: 1, scope: .whileConnected)
        
        let airportObservable = state.airports
            .skip(1)
            .asObservable()
        
        // 合并信号源
        let sections = Observable.combineLatest(searchTextAbservable, airportObservable)
            .map({ (searchKey, airports) in
                airports.filter { airport -> Bool in
                    let result =  !searchKey.isEmpty &&
                        airport.city
                            .lowercased()
                            .replacingOccurrences(of: " ", with: "")
                            .hasPrefix(searchKey.lowercased())
                    return result
                }
            })
            .map({
                SearchCityViewModel.uniqueElimentsFrom(array: $0.compactMap({ CityViewModel(model: $0) }))
            }).map({
                // 转换为 CityItemsSection
                [CityItemsSection(model: 0, items: $0)]
            }).asDriver(onErrorJustReturn: [])
        
        // 作为 output 返回, cityes 可以直接用于驱动 tableview
        return (
            cityes: sections, ()
        )
    }
    
    func process() {

        self.airportService
            .fechAirports().map({
                Set($0)
            })
            .map({ [state] in
                state.airports.accept($0)
            })
            .subscribe { (sigle) in
                print("加载完成")
            }
            .disposed(by: bag)

        // 监听到 cell 被点击事件, 处理过数据后，发出一个cell 被点击的信号
        self.input.citySelected
            .map({ $0.city })
            .withLatestFrom(state.airports.asDriver()) { ($0, $1) }
            .map({ (city, airports) in
                airports.filter({ $0.city == city })
            }).map({ [routingAction] in
                // cell 被点击时发出信号
                routingAction.citySelectedRelay.accept($0)
            })
            .drive()
            .disposed(by: bag)
        
    }
}


extension SearchCityViewModel {
    // 用Set唯一特性 做去重
    static func uniqueElimentsFrom(array: [CityViewModel]) -> [CityViewModel] {
        var set = Set<CityViewModel>()
        let result = array.filter({
            guard !set.contains($0) else {
                return false
            }
            set.insert($0)
            return true
        })
        return result
    }
}
